package org.udr.datamodel;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;

public class DataModelExtractor {

	private Configuration config;

	private Map<String, Entity> processedEntities = new  HashMap<String, Entity>(); 
	
	public DataModel extract(Configuration config, String modelName) {
		this.config = config;
		Iterator<PersistentClass> classMappings = config.getClassMappings();
		PersistentClass currentClassMapping = classMappings.next();
		processClass(currentClassMapping);
		DataModel dataModel = new DataModel("testModel",processedEntities);
		return dataModel;
	}

	private Entity processClass(PersistentClass classMapping) {
		if (!processedEntities.containsKey(classMapping.getEntityName())) {
			Entity entity = new Entity(classMapping.getEntityName());
			processedEntities.put(classMapping.getEntityName(), entity);
			processProperties(entity, classMapping.getPropertyIterator());
			return entity;
		} else {
			return processedEntities.get(classMapping.getEntityName());
		}
	}

	private void processProperties(Entity current, Iterator<Property> propertiesIterator) {
		while (propertiesIterator.hasNext()) {
			Property property = propertiesIterator.next();
			Value val = property.getValue();
			String referencedEntityName;
			
			// one-to-one/many-to-one
			if (val instanceof ToOne) {
				referencedEntityName = ((ToOne) val).getReferencedEntityName();
				addReferencedEntity(current, property, referencedEntityName);
				continue;
			}

			// component
			if (val instanceof Component) {
				addComponent(current,property, (Component) val);
				continue;
			}

			// collections
			if (val instanceof Collection) {
				addCollection(current, property, (Collection) val);
				continue;
			}

			// primitive
			if (val instanceof SimpleValue) {
				addSimple(current, property, val);
				continue;
			}

			// QueryList and other things will be taken care of elsewhere
			continue;
		}
	}

	private void addSimple(Entity ownerEntity, Property myProp, Value val) {
		org.udr.datamodel.Property property = new org.udr.datamodel.Property(ownerEntity, 
				myProp.getName());
		ownerEntity.addAttribute(property);
	}

	private void addCollection(Entity ownerEntity, Property property, Collection val) {
		Value elem = val.getElement();
		String name = val.getClass().getName();
		String referencedEntityName;

		if (elem instanceof OneToMany) {
			referencedEntityName = ((OneToMany) elem).getReferencedEntityName();
			addReferencedEntity(ownerEntity, property, referencedEntityName);
			return;
		}

		if (elem instanceof ToOne) {
			referencedEntityName = ((ToOne) elem).getReferencedEntityName();
			addReferencedEntity(ownerEntity, property,referencedEntityName);
			return;
		}

		if (elem instanceof Component) {
			System.out.println(name + " has-element Component");
			return;
		}

		if (elem instanceof SimpleValue) {
			System.out.println(name + " has-element SimpleValue");
			return;
		}
	}

	private void addComponent(Entity rootEntity, Property myProp, Component val) {
		Component val2 = val;
	}

	private void addReferencedEntity(Entity ownerEntity, Property myProp, String referencedEntityName) {
		PersistentClass classMapping = config.getClassMapping(referencedEntityName);
	    Entity extractEntity = processClass(classMapping);
	    Association asn = new Association(ownerEntity, myProp.getName(),extractEntity);
	    ownerEntity.addAttribute(asn);
	}

}